/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-07-14 09:36:56
 * @LastEditTime: 2021-08-09 15:17:54
 * @Description:  This files is for 
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 */
#include "ft_assert.h"
#include "timer_tacho_hw.h"
#include "timer_tacho.h"

void TimerSoftwareReset(TimerTachoCtrl *pCtrl)
{
    FT_ASSERTVOID(pCtrl);

    u32 RegVal = TIMER_CTRL_READ(pCtrl);
    u32 Timeout = 0;
    RegVal |= TIMER_REG_TACHO_RESET;
    TIMER_CTRL_WRITE(pCtrl, RegVal);

    do
    {
        RegVal = TIMER_CTRL_READ(pCtrl);
        Timeout++;
    }while((RegVal & TIMER_REG_TACHO_RESET) && (Timeout < TIMER_TIMEOUT));

    if (Timeout >= TIMER_TIMEOUT)
    {
        TIMER_ERROR("Software Reset Failed!!!");
    }
}

void TimerStart(TimerTachoCtrl *pCtrl)
{
    FT_ASSERTVOID(pCtrl);
    u32 RegVal;

    RegVal = TIMER_CTRL_READ(pCtrl);
    if (TIMER_WORK_MODE_TIMER == pCtrl->Config.WorkMode)
    {
        RegVal |= TIMER_REG_ENABLE;
    }
    else
    {
        /* for tacho mode and capture mode */
        RegVal |= TIMER_REG_ENABLE | TACHO_REG_CAP_IN_ENABLE;
    }

    TIMER_CTRL_WRITE(pCtrl, RegVal);
}

void TimerStop(TimerTachoCtrl *pCtrl)
{
    FT_ASSERTVOID(pCtrl);

    u32 RegVal = TIMER_CTRL_READ(pCtrl);

    if (TIMER_WORK_MODE_TIMER == pCtrl->Config.WorkMode)
    {
        RegVal &= (~TIMER_REG_ENABLE);
    }
    else
    {
        /* for tacho mode and capture mode */
        RegVal &= (~TIMER_REG_ENABLE) & (~TACHO_REG_CAP_IN_ENABLE);
    }
    
    TIMER_CTRL_WRITE(pCtrl, RegVal);
}

static void TimerForceLoad(TimerTachoCtrl *pCtrl)
{
    u32 RegVal;

    RegVal = TIMER_CTRL_READ(pCtrl);
    RegVal |= TIMER_REG_TACHO_FORCE_LOAD;
    TIMER_CTRL_WRITE(pCtrl, RegVal);
}

u32 TimerSetPeriod32(TimerTachoCtrl *pCtrl, u32 NewCmpL)
{
    FT_ASSERTZERONUM(pCtrl);

    if (TIMER_REG_CNT_SERIES_64BIT == pCtrl->Config.TimerBits)
    {
        return TIMER_INVALID_PARM;
    }

    /* update cmp val */
    TIMER_CMPL_WRITE(pCtrl, NewCmpL);

    return TIMER_SUCCESS;
}

u32 TimerSetPeriod64(TimerTachoCtrl *pCtrl, u64 ticks)
{
    u32 lowCmp;
    u32 upCmp;
    FT_ASSERTZERONUM(pCtrl);

    if (TIMER_REG_CNT_SERIES_32BIT == pCtrl->Config.TimerBits)
    {
        return TIMER_INVALID_PARM;
    }    

    lowCmp = (u32)(GENMASK_ULL(31, 0) & ticks);
    upCmp  = (u32)((GENMASK_ULL(63, 32) & ticks) >> 32);

    /* MUST write low 32 bit first !!! */
    TIMER_CMPL_WRITE(pCtrl, lowCmp);
    TIMER_CMPU_WRITE(pCtrl, upCmp);
    
    return TIMER_SUCCESS;
}

inline void TimerSetStartVal(TimerTachoCtrl *pCtrl, u32 cnt)
{
    TIMER_STAR_WRITE(pCtrl, cnt);
    /* set force_load=1，invalid previous cmp val, 
       otherwise the previous cmp val still work */
    TimerForceLoad(pCtrl);    
    TIMER_DEBUG_I("set start val 0x%x", TIMER_STAR_READ(pCtrl));
}

u32 TimerGetCurCnt32(TimerTachoCtrl *pCtrl)
{
    return TIMER_CNTL_READ(pCtrl);
}

inline u64 TimerGetCurCnt64(TimerTachoCtrl *pCtrl)
{
    u64 cnt = 0;
    /* must read lower 32 bits first */
    cnt |= (u64)TIMER_CNTL_READ(pCtrl);
    cnt |= (u64)(((u64)TIMER_CNTU_READ(pCtrl)) << 32);
    return cnt;
}

extern void TimerDefaultEvtCallback(void *pCtrl);
extern void TimerEnableIntr(TimerTachoCtrl *pCtrl);
extern void TimerDisableIntr(TimerTachoCtrl *pCtrl);
u32 TimerInit(TimerTachoCtrl *pCtrl, const TimerTachoConfig *pConfig)
{
    FT_ASSERTZERONUM(pCtrl && pConfig);

    u32 RegVal = 0;
    u32 Ret = TIMER_SUCCESS;

    if ((TIMER_ONCE_CMP == pConfig->CmpType) && 
        (TIMER_FREE_RUN != pConfig->TimerMode))
    {
        TIMER_ERROR("time mode shall be free-run when use once timer!!");
        return TIMER_INVALID_PARM;
    }

    pCtrl->IsReady = FALSE;

    TimerDisableIntr(pCtrl);

    if (TIMER_WORK_MODE_TIMER == pConfig->WorkMode)
    {
        RegVal |= TIMER_REG_TACHO_MODE_TIMER;
    }
    else 
    {
        TIMER_ERROR("not support");
        return TIMER_INVALID_PARM;
    }
        
    if (TIMER_FREE_RUN == pConfig->TimerMode)
    {
        RegVal |= TIMER_REG_CNT_FREERUN;
    }
    else if (TIMER_RESTART == pConfig->TimerMode)
    {
        RegVal |= TIMER_REG_CNT_RESTART;
    }
    else
    {
        TIMER_ERROR("invalid input");
        return TIMER_INVALID_PARM;
    }
    
    if (TIMER_32_BITS == pConfig->TimerBits)
    {
        RegVal |= TIMER_REG_CNT_SERIES_32BIT;
    }
    else if (TIMER_64_BITS == pConfig->TimerBits)
    {
        RegVal |= TIMER_REG_CNT_SERIES_64BIT;
    }
    else
    {
        TIMER_ERROR("invalid input");
        return TIMER_INVALID_PARM;
    }    

    if (TIMER_ONCE_CMP == pConfig->CmpType)
    {
        RegVal |= TIMER_REG_MODE_ONCE;
    }
    else if (TIMER_CYC_CMP == pConfig->CmpType)
    {
        RegVal |= TIMER_REG_MODE_CYC;
    }
    else
    {
        TIMER_ERROR("invalid input");
        return TIMER_INVALID_PARM;
    }       
    
    if (TRUE == pConfig->ForceLoad)
    {
        RegVal |= TIMER_REG_TACHO_FORCE_LOAD;
    }

    if (TRUE == pConfig->ClearCnt)
    {
        RegVal |= TIMER_REG_CNT_CLR;
    }
    
    TIMER_CTRL_WRITE(pCtrl, RegVal);

    if (TIMER_SUCCESS == Ret)
    {
        //use input config
        if (pConfig != &pCtrl->Config)
        {
            pCtrl->Config = *pConfig;
        }

        pCtrl->IsReady = TRUE;
        TimerEnableIntr(pCtrl);   
    }

    return Ret;
}

void TimeSettingDump(const TimerTachoCtrl *pCtrl)
{
    FT_ASSERTVOID(pCtrl);
    u32 CtrlReg = TIMER_CTRL_READ(pCtrl);
    boolean is64Bit = ((CtrlReg & TIMER_REG_CNT_SERIES_64BIT) != 0);

    printf("ctrl: \r\n");
    printf("===%d-bit timer\r\n", is64Bit?64:32);
    printf("===timer enabled: %d\r\n", (CtrlReg & TIMER_REG_ENABLE)?1:0);
    printf("===timer mode: %d\r\n", (CtrlReg & TIMER_REG_TACHO_MODE_TIMER)?1:0);
    printf("===once timer: %d\r\n", (CtrlReg & TIMER_REG_MODE_ONCE)?1:0);
    printf("===restart mode: %d\r\n", (CtrlReg & TIMER_REG_CNT_RESTART)?1:0);
    printf("===in reset: %d\r\n", (CtrlReg & TIMER_REG_TACHO_RESET)?1:0);
    printf("===force load: %d\r\n", (CtrlReg & TIMER_REG_TACHO_FORCE_LOAD)?1:0);
    printf("===clear cnt: %d\r\n", (CtrlReg & TIMER_REG_CNT_CLR)?1:0);

    printf("start cnt: 0x%08x\r\n",  TIMER_STAR_READ(pCtrl));
    if (is64Bit)
    {
        printf("cmp low: 0x%08x", TIMER_CMPL_READ(pCtrl));
        printf("high: 0x%08x\r\n", TIMER_CMPU_READ(pCtrl));
        printf("cur cnt: low: 0x%08x", TIMER_CNTL_READ(pCtrl));
        printf("high: 0x%08x\r\n", TIMER_CNTU_READ(pCtrl));
    }
    else
    {
        printf("cmp low: 0x%08x\r\n", TIMER_CMPL_READ(pCtrl));
        printf("cur cnt: low: 0x%08x", TIMER_CNTL_READ(pCtrl));
    }

    printf("intr mask: 0x%08x\r\n", TIMER_INTR_M_READ(pCtrl));
    printf("intr status: 0x%08x\r\n", TIMER_INTR_S_READ(pCtrl));
}